use std::{
    cmp,
    io::{self, Result},
    net::{self, TcpListener},
    sync::{Arc, Mutex},
};

use socket2::Protocol;
use tiny_r_net::{Server, ServerBuilder};
#[derive(Debug)]
struct Socket {
    schema: &'static str,
    addr: net::SocketAddr,
}

#[derive(Debug)]
struct Config {
    host: Option<String>,
}

pub struct HttpServer {
    config: Arc<Mutex<Config>>,
    sockets: Vec<Socket>,
    backlog: u32,
    builder: ServerBuilder,
}

impl HttpServer {
    /// 实例化，创建HttpServer
    pub fn new() -> Self {
        HttpServer {
            config: Arc::new(Mutex::new(Config { host: None })),
            sockets: Vec::new(),
            backlog: 1024,
            builder: ServerBuilder::default(),
        }
    }

    /// 绑定服务，创建监听
    pub fn bind<A: net::ToSocketAddrs>(mut self, addrs: A) -> io::Result<Self> {
        let sockets = bind_addrs(addrs, self.backlog)?;
        for lst in sockets {
            self = self.listen(lst)?;
        }
        Ok(self)
    }

    /// 绑定到存在的listener，以接收即将到来的连接请求
    ///
    /// 将所有权交给 listen
    pub fn listen(mut self, lst: net::TcpListener) -> io::Result<Self> {
        // 复制配置
        let cfg = Arc::clone(&self.config);
        //TODO: let factory
        let addr = lst.local_addr()?;
        self.sockets.push(Socket {
            addr,
            schema: "http",
        });
        //TODO: on_connect_fn

        self.builder = self
            .builder
            .listen(format!("actix-web-service-{}", addr), lst)?;
        Ok(self)
    }
}

impl HttpServer {
    pub fn run(self) -> Server {
        self.builder.run()
    }
}

/// 将TCP 监听绑定到 从addrs中解析的socket地址
fn bind_addrs(addrs: impl net::ToSocketAddrs, backlog: u32) -> io::Result<Vec<net::TcpListener>> {
    let mut err = None;
    let mut success = false;
    let mut sockets = Vec::new();

    // 遍历获取地址信息，尝试绑定监听,默认只迭代一次？否则标识不对呀
    for addr in addrs.to_socket_addrs()? {
        match create_tcp_listener(addr, backlog) {
            Ok(lst) => {
                success = true;
                sockets.push(lst);
            }
            Err(error) => err = Some(error),
        }
    }
    // 成功则返回列表
    if success {
        Ok(sockets)
    } else if let Some(err) = err.take() {
        Err(err)
    } else {
        Err(io::Error::new(
            io::ErrorKind::Other,
            "Can not bind to address.",
        ))
    }
}

fn create_tcp_listener(addr: net::SocketAddr, backlog: u32) -> io::Result<TcpListener> {
    use socket2::{Domain, Socket, Type};
    let domain = Domain::for_address(addr);
    let socket = Socket::new(domain, Type::STREAM, Some(Protocol::TCP))?;
    #[cfg(not(windows))]
    {
        /// 在 Linux、macOS 等非 Windows 系统上，SO_REUSEADDR 选项是常见的做法，可以提高服务器的可用性和稳定性
        socket.set_reuse_address(true)?;
    }
    // 绑定地址
    socket.bind(&addr.into())?;
    let backlog = cmp::min(backlog, i32::MAX as u32) as i32;
    socket.listen(backlog)?;
    Ok(net::TcpListener::from(socket))
}
